#include <sys/epoll.h>
#include <unistd.h>
#include <stdlib.h>
#include "Log.hpp"
#include "EventLoop.hpp"

#define EPOLL_EVENTS_SIZE 1024

using namespace ns_log;

EpollDispachter::EpollDispachter(EventLoop *evLoop)
    : Dispatcher(evLoop)
{
    _epfd = epoll_create(1);
    if (_epfd < 0)
    {
        LOG(ERROR) << "epoll_create error\n";
        exit(-1);
    }
    // epoll_event *events = (epoll_event *)calloc(EPOLL_EVENTS_SIZE, sizeof(epoll_event));
    _events = new epoll_event[EPOLL_EVENTS_SIZE];
}
int EpollDispachter::epollCtl(int op)
{
    epoll_event event;
    event.data.fd = _channel->_fd;
    int events = 0;
    if (_channel->_events & ReadEvent)
        events |= EPOLLIN;
    if (_channel->_events & WriteEvent)
        events |= EPOLLOUT;
    event.events = events;

    return epoll_ctl(_epfd, op, _channel->_fd, &event);
}
int EpollDispachter::add()
{
    // LOG(INFO) << evLoop->_threadName << ":" << channel->_fd << "\n";

    int ret = epollCtl(EPOLL_CTL_ADD);
    if (ret < 0)
    {
        LOG(ERROR) << "epoll_ctl add error\n";
        exit(-1);
    }
    return ret;
}
int EpollDispachter::remove()
{
    int ret = epollCtl(EPOLL_CTL_DEL);
    if (ret < 0)
    {
        LOG(ERROR) << "epoll_ctl add error\n";
        exit(-1);
    }
    return ret;
}
int EpollDispachter::modify()
{
    int ret = epollCtl(EPOLL_CTL_MOD);
    if (ret < 0)
    {
        LOG(ERROR) << "epoll_ctl add error\n";
        exit(-1);
    }
    return ret;
}
int EpollDispachter::dispatch(int timeout)
{
    int ret = epoll_wait(_epfd, _events, EPOLL_EVENTS_SIZE, timeout * 1000);
    // LOG(INFO) << evLoop->_threadName << ":" << ret << "\n";
    for (int i = 0; i < ret; ++i)
    {
        int fd = _events[i].data.fd;
        int events = _events[i].events;
        // 异常
        if (events & EPOLLERR || events & EPOLLHUP)
        {
            // 对方断开连接 删除fd 通过channelMap获得
            _channel = _evLoop->_channelMap[fd];
            remove();
            close(fd);
            continue;
        }
        if (events & EPOLLIN)
        {
            _evLoop->activate(fd, ReadEvent);
        }
        if (events & EPOLLOUT)
        {
            _evLoop->activate(fd, WriteEvent);
        }
    }
    return 0;
}
EpollDispachter::~EpollDispachter()
{
    if (_events)
        delete[] _events;
    close(_epfd);
}